#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
#include <sys/ioctl.h>
#include<sys/time.h>
#include "unixsocket.h"

void *clinet_1(void *arg)
{
    int sockfd;
    int confd;
    struct sockaddr_un  serveraddr;

    UNIX_DATA data;

    if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        err_info("fail to socket");
    }

    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path, UNIXSOCKET_PATH);

    if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) {
        err_info("fail to connect");
    }

    dbg_info("id 1 to be init socket and id table \n");
    data.type = init;
    data.id_from = 0x01;
    send(sockfd, (void *)&data, sizeof(UNIX_DATA), 0);

    while (1) {

        recv(sockfd, (void *)&data,  sizeof(UNIX_DATA), 0);
		gettimeofday(&end1, NULL);
        dbg_info("client1 recv data id from:%d id to: %d type:%d len:%d buffer:%s\n", data.id_from, data.id_to[0], data.type,
                 data.len, data.buffer);
        comm_info("use time %ld us\n", 10000000 * (end1.tv_sec - start1.tv_sec) + (end1.tv_usec - start1.tv_usec));

    }

    close(sockfd);
}

void *clinet_2(void *arg)
{
    int sockfd;
    int confd;
    struct sockaddr_un  serveraddr;

    UNIX_DATA data;

    if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        err_info("fail to socket");
    }

    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path, UNIXSOCKET_PATH);

    if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) {
        err_info("fail to connect");
    }

    dbg_info("id 2 to be init socket and id table \n");
    data.type = init;
    data.id_from = 0x02;
    send(sockfd, (void *)&data, sizeof(UNIX_DATA), 0);

    while (1) {

        recv(sockfd, (void *)&data,  sizeof(UNIX_DATA), 0);
        gettimeofday(&end2, NULL);

        dbg_info("client2 recv data id from:%d id to: %d type:%d len:%d buffer:%s\n", data.id_from, data.id_to[0], data.type,
                 data.len, data.buffer);

        comm_info("use time %ld us\n", 10000000 * (end2.tv_sec - start2.tv_sec) + (end2.tv_usec - start2.tv_usec));
    }

    close(sockfd);
}

void *clinet_3(void *arg)
{
    int sockfd;
    int confd;
    struct sockaddr_un  serveraddr;

    UNIX_DATA data;

    if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        err_info("fail to socket");
    }

    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path, UNIXSOCKET_PATH);

    if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) {
        err_info("fail to connect");
    }

    dbg_info("id 3 to be init socket and id table \n");
    data.type = init;
    data.id_from = 0x03;
    send(sockfd, (void *)&data, sizeof(UNIX_DATA), 0);

    while (1) {

        recv(sockfd, (void *)&data,  sizeof(UNIX_DATA), 0);
		gettimeofday(&end3, NULL);
	
        dbg_info("client3 recv data id from:%d id to: %d type:%d len:%d buffer:%s\n", data.id_from, data.id_to[0], data.type,
                 data.len, data.buffer);
		
        comm_info("use time %ld us\n", 10000000 * (end3.tv_sec - start3.tv_sec) + (end3.tv_usec - start3.tv_usec));

    }
    close(sockfd);
}

void *clinet_cmd(void *arg)
{
    int sockfd;
    int confd;
    struct sockaddr_un  serveraddr;

    UNIX_DATA data;

    data.id_from = 0x04;
    data.type = c2mc;
    strcpy(data.buffer, "i am client cmd");
    data.len = strlen(data.buffer);

    if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        err_info("fail to socket");
    }

    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path, UNIXSOCKET_PATH);

    if (connect(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) {
        err_info("fail to connect");
    }

    /*init cmd*/
    dbg_info("id 4 to be init socket and id table \n");
    data.type = init;
    data.id_from = 0x04;
    send(sockfd, (void *)&data, sizeof(UNIX_DATA), 0);

    data.type = c2mc;

    sleep(1);

    unsigned int i = 1;
    while (1) {
        data.id_to[0] = 1;
        data.id_to[1] = 2;
		data.id_to[2] = 3;

       // if (i == 1)
            gettimeofday(&start1, NULL);

       // if (i == 2)
            gettimeofday(&start2, NULL);

       //  if (i == 3)
            gettimeofday(&start3, NULL);

        send(sockfd, (void *)&data, sizeof(UNIX_DATA), 0);
        i++;

		sprintf(data.buffer,"i am client cmd %d ",i);
		data.len = strlen(data.buffer);

		usleep(1000);
    }
    close(sockfd);
}

int analyse(int sockfd, UNIX_DATA data)
{
    char fd_client[10];
	int i;
	
    /*init*/
    if (data.type == init) {
        fd_client[data.id_from] = sockfd;
        printf(" id : %d <--> socket %d\n", data.id_from, sockfd);
    }

    /*c 2 s*/
    if (data.type == c2s) {
        switch (data.id_from) {
            case 0x01:
                //	 send(sockfd, (void *)&data, sizeof(data), 0);
                break;
            case 0x02:
                //	  send(sockfd, (void *)&data, sizeof(data), 0);
                break;
            case 0x03:
                //	 send(sockfd, (void *)&data, sizeof(data), 0);
                break;
            default:
                printf("can't find the id from: %d\n", data.id_from);
                break;
        }
    }

    /*c 2 c*/
    if (data.type == c2c) {
    

        switch (data.id_to[0]) {
            case 0x01:
                sockfd= fd_client[data.id_to[0]];
                send(sockfd, (void *)&data, sizeof(data), 0);
                break;
            case 0x02:
                sockfd = fd_client[data.id_to[0]];
                send(sockfd, (void *)&data, sizeof(data), 0);
                break;
            case 0x03:
                sockfd = fd_client[data.id_to[0]];
                send(sockfd, (void *)&data, sizeof(data), 0);
                break;
            default:
                printf("can't find the id to: %d\n", data.id_to[0]);
                break;
        }
 
    }

    /*c 2 mc*/
    if (data.type == c2mc) {
        dbg_info("recv data id from:%d	type:%d len:%d	buffer:%s\n", data.id_from, data.type, data.len,
                 data.buffer);

        for (i = 0; i < 10; i++) {
            if (data.id_to[i] == 0)
                continue;

            dbg_info("id to:%d\n", data.id_to[i]);

            UNIX_DATA senddata;

            senddata = data;
            switch (data.id_to[i]) {
                case 0x01:
                    sockfd = fd_client[data.id_to[i]];
                    senddata.id_to[0] = data.id_to[i];
                    send(sockfd, (void *)&senddata, sizeof(senddata), 0);
                    break;
                case 0x02:
                    sockfd = fd_client[data.id_to[i]];
                    senddata.id_to[0] = data.id_to[i];
                    send(sockfd, (void *)&senddata, sizeof(senddata), 0);
                    break;
                case 0x03:
                    sockfd = fd_client[data.id_to[i]];
                    senddata.id_to[0] = data.id_to[i];
                    send(sockfd, (void *)&senddata, sizeof(senddata), 0);
                    break;
                default:
                    printf("can't find the id to: %d\n", data.id_to[i]);
                    break;
            }
        }
    }
}

int main()
{
    pthread_t pid_client1, pid_client2, pid_client3, pid_client_cmd;
    int sockfd;
    int confd;
    struct sockaddr_un  serveraddr, clientaddr;
	UNIX_DATA data;

    unlink(UNIXSOCKET_PATH);

    if ((sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
        err_info("fail to socket\n");
    }

    serveraddr.sun_family = AF_UNIX;
    strcpy(serveraddr.sun_path, UNIXSOCKET_PATH);

    if (bind(sockfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) {
        err_info("fail to bind\n");
    }

    if (listen(sockfd, 10) < 0) {
        err_info("fail to listen\n");
    }

    pthread_create(&pid_client1, NULL, clinet_1, NULL);
    pthread_create(&pid_client2, NULL, clinet_2, NULL);
    pthread_create(&pid_client3, NULL, clinet_3, NULL);
    pthread_create(&pid_client_cmd, NULL, clinet_cmd, NULL);


    socklen_t addrlen = sizeof(struct sockaddr);

    fd_set readfds, testfds;
    FD_ZERO(&readfds);
    FD_SET(sockfd, &readfds);//将服务器端socket加入到集合中

    while (1) {
        int fd;
        int nread;
        testfds = readfds;

        /*无限期阻塞，并测试文件描述符变动 */
        int result = select(100, &testfds, (fd_set *)0, (fd_set *)0, (struct timeval *) 0);
        if (result < 1) {
            perror("server5");
            close(sockfd);
            exit(1);
        }

        /*扫描所有的文件描述*/
        for (fd = 0; fd < 100; fd++) {
	
            /*找到相关文件描述*/
            if (FD_ISSET(fd, &testfds)) {
                /*判断是否为服务器套接字，是则表示为客户请求连接*/
                if (fd == sockfd) {
                    addrlen = sizeof(clientaddr);
                    if ((confd = accept(sockfd, (struct sockaddr *)&clientaddr, &addrlen)) < 0) {
                        err_info("fail to accept");
                    }

                    FD_SET(confd, &readfds);//将客户端socket加入到集合中
                }

                /*客户端socket中有数据请*/
                else {
                    ioctl(fd, FIONREAD, &nread);//取得数据量交给nread

                    /*客户数据请求完毕，关闭套接字，从集合中清除相应描述符 */
                    if (nread == 0) {
                        close(fd);
                        FD_CLR(fd, &readfds);
/*
                        for (i = 0; i < 10; i++) {
                            if (fd_client[i] == fd)
                                fd_client[i] = 0;
                        }
*/
                        printf("removing client on fd %d\n", fd);
                    }
                    /*处理客户数据请求*/
                    else {

                        recv(fd, (void *)&data, sizeof(data), 0);
						analyse(fd,data);
					}
                }
            }
        }
    }
    close(sockfd);
}

